﻿using System.Windows.Interactivity;
using System.Windows;
using System.ComponentModel;
using System.Windows.Data;

namespace ZOIL.Library.Behaviors
{
    public class PluginBehavior<T, P> : PluginBehavior<T> where P:Behavior<T> where T:DependencyObject
    {
        #region ParentBehavior

        public new P ParentBehavior
        {
            get 
            { 
                return (P)base.ParentBehavior; 
            }
            set
            {
                var old = ParentBehavior;
                base.ParentBehavior = value;
                OnParentBehaviorChanged(old, ParentBehavior);
            }
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the ParentBehavior property.
        /// </summary>
        protected virtual void OnParentBehaviorChanged(P oldParentBehavior, P newParentBehavior)
        {
        }

        protected override void OnParentBehaviorChangedImpl(object oldParentBehavior, object newParentBehavior)
        {
            OnParentBehaviorChanged((P)oldParentBehavior, (P)newParentBehavior);
        }

        #endregion
    }

    public class PluginBehavior<T> : Behavior<T>, INotifyPropertyChanged where T : DependencyObject
    {
        #region dependency properties

        #region IsEnabled

        /// <summary>
        /// IsEnabled Dependency Property
        /// </summary>
        public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register("IsEnabled", typeof(bool), typeof(PluginBehavior<T>), new FrameworkPropertyMetadata(false, OnIsEnabledChanged));

        /// <summary>
        /// Gets or sets the IsEnabled property. This dependency property 
        /// indicates ....
        /// </summary>
        public bool IsEnabled
        {
            get
            {
                var value = (bool)GetValue(IsEnabledProperty);
                return value;
            }

            set
            {
                SetValue(IsEnabledProperty, value);
            }
        }

        /// <summary>
        /// Handles changes to the IsEnabled property.
        /// </summary>
        private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var target = (PluginBehavior<T>)d;
            var oldIsEnabled = (bool)e.OldValue;
            var newIsEnabled = target.IsEnabled;
            target.OnIsEnabledChanged(oldIsEnabled, newIsEnabled);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the IsEnabled property.
        /// </summary>
        protected virtual void OnIsEnabledChanged(bool oldIsEnabled, bool newIsEnabled)
        {
        }

        #endregion

        #endregion

        #region ParentBehavior

        private object _parentBehavior;

        /// <summary>
        /// Gets or sets the ParentBehavior property. This observable property 
        /// indicates
        /// </summary>
        public object ParentBehavior
        {
            get { return _parentBehavior; }
            set
            {
                if (_parentBehavior != value)
                {
                    var old = _parentBehavior;
                    _parentBehavior = value;
                    OnParentBehaviorChanged(old, value);
                    RaisePropertyChanged("ParentBehavior");
                }
            }
        }

        /// <summary>
        /// Connects is enabled property of the parent to this plugin behavior.
        /// </summary>
        protected void OnParentBehaviorChanged(object oldParentBehavior, object newParentBehavior)
        {
            var oldRuntimeBehavior = newParentBehavior as IRuntimeBehavior;
            if (oldRuntimeBehavior != null)
                ClearParentBehaviorBinding(IsEnabledProperty);

            var newRuntimeBehavior = newParentBehavior as IRuntimeBehavior;
            if (newRuntimeBehavior != null)
                CreateOneWayParentBehaviorBinding(newRuntimeBehavior.IsEnabledDependencyProperty, IsEnabledProperty);

            OnParentBehaviorChangedImpl(oldParentBehavior, newParentBehavior);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the ParentBehavior property.
        /// </summary>
        protected virtual void OnParentBehaviorChangedImpl(object oldParentBehavior, object newParentBehavior)
        {
        }

        #endregion

        #region Binding Helper

        protected void CreateTwoWayParentBehaviorBinding(DependencyProperty parentProperty, DependencyProperty pluginProperty)
        {
            var binding = new Binding
            {
                Source = ParentBehavior,
                Path = new PropertyPath(parentProperty),
                Mode = BindingMode.TwoWay
            };
            BindingOperations.SetBinding(this, pluginProperty, binding);
        }

        protected void CreateOneWayParentBehaviorBinding(DependencyProperty parentProperty, DependencyProperty pluginProperty)
        {
            var binding = new Binding
            {
                Source = ParentBehavior,
                Path = new PropertyPath(parentProperty),
                Mode = BindingMode.OneWay
            };
            BindingOperations.SetBinding(this, pluginProperty, binding);
        }

        protected void ClearParentBehaviorBinding(DependencyProperty pluginProperty)
        {
            BindingOperations.ClearBinding(this, pluginProperty);
        } 

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChanged(string name)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }

        #endregion
    }
}
